/*
 * Copyright 2016-2019 yoara
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package yhao.infra.web.filter;

import org.apache.commons.lang.StringUtils;
import yhao.infra.common.ContextHolder;
import yhao.infra.web.common.session.HttpServletRequestWrapper;
import yhao.infra.web.common.session.SessionService;
import yhao.infra.web.constants.WebCommonConstants;

import javax.servlet.*;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

/**
 * 统一session过滤器
 *
 * @author yoara
 */
public class RedisSessionFilter extends HttpServlet implements Filter {
	private static final long serialVersionUID = -365105405910803550L;
	private static final String UN_NEED_SID = "no_sid";
	private static String sessionId = "sid";
	private static String cookieDomain = "";
	private static String cookiePath = "/";
	private static String sessionIdHeaderKey = "";
	private static boolean hasSessionIdHeaderKey;
	private static String sessionSourceHeaderKey = "";
	private static boolean hasSessionSourceHeaderKey;
	private static Map<String,Integer> sessionSourceMap = new HashMap<>();

	/**
	 * 增加来源数据信息
	 * @param sourceKey 配置session来源键
	 * @param expireTime 配置session有效期时间，单位为秒
	 */
	public static void addSessionSource(String sourceKey,Integer expireTime){
		sessionSourceMap.put(sourceKey,expireTime);
	}

	public void doFilter(ServletRequest servletRequest,
			ServletResponse servletResponse, FilterChain filterChain)
			throws IOException, ServletException {
		HttpServletRequest request = (HttpServletRequest) servletRequest;
		HttpServletResponse response = (HttpServletResponse) servletResponse;
		if (request.getServletPath().contains(".")) {
			filterChain.doFilter(request, response);
			return;
		}
		//根据请求来源设置超时时间
		if(hasSessionSourceHeaderKey){
			makeSessionExpireTime(request);
		}
		String sid = null;
		if(hasSessionIdHeaderKey){
			sid = request.getHeader(sessionIdHeaderKey);
		}
		if(StringUtils.isEmpty(sid)){
			sid = getIdInCookie(request, sessionId);
		}
		//若sid不存在，则依照uuid规则新生成一条sid
		if (StringUtils.isEmpty(sid)) {
			sid = java.util.UUID.randomUUID().toString();
			if(StringUtils.isEmpty(request.getParameter(UN_NEED_SID))){
				setSidInCookie(response, sid);
			}
		}
		request.setAttribute(WebCommonConstants.REQUEST_SESSION_ID, sid);
		//做session延时操作
		SessionService.getInstance().expireSession(sid);
		filterChain.doFilter(new HttpServletRequestWrapper(sid, request), servletResponse);
	}

	private void makeSessionExpireTime(HttpServletRequest request) {
		Integer expireTime = sessionSourceMap.get(request.getHeader(sessionSourceHeaderKey));
		if(expireTime!=null){
			ContextHolder.setContextData(SessionService.SessionExpireKey.class,expireTime);
		}else{
			ContextHolder.removeContextData(SessionService.SessionExpireKey.class);
		}
	}

	private String getIdInCookie(HttpServletRequest request, String key) {
		Cookie[] cookies = request.getCookies();
		if(cookies!=null && cookies.length>0){
			for(Cookie sCookie:cookies){
				if (sCookie.getName().equals(key)) {
					return sCookie.getValue();
				}
			}
		}
		return null;
	}

	private static void setSidInCookie(HttpServletResponse response, String sid) {
		Cookie sidCookies = new Cookie(sessionId, sid);
		sidCookies.setMaxAge(-1);
		if (StringUtils.isNotEmpty(cookieDomain)) {
			sidCookies.setDomain(cookieDomain);
		}
		sidCookies.setPath(cookiePath);
		response.addCookie(sidCookies);
	}

	public void init(FilterConfig filterConfig){
		String filterSessionId = filterConfig.getInitParameter("sessionId");
		if (StringUtils.isNotBlank(filterSessionId)) {
			this.sessionId = filterSessionId;
		}
		String filterDomain = filterConfig.getInitParameter("cookieDomain");
		if (StringUtils.isNotBlank(filterDomain)) {
			this.cookieDomain = filterDomain;
		}
		String filterCookiePath = filterConfig.getInitParameter("cookiePath");
		if (StringUtils.isNotBlank(filterCookiePath)) {
			this.cookiePath = filterCookiePath;
		}
		String filterSessionIdHeaderKey = filterConfig.getInitParameter("sessionIdHeaderKey");
		if (StringUtils.isNotBlank(filterSessionIdHeaderKey)) {
			this.sessionIdHeaderKey = filterSessionIdHeaderKey;
			this.hasSessionIdHeaderKey = true;
		}
		String filterSessionSourceHeaderKey = filterConfig.getInitParameter("sessionSourceHeaderKey");
		if (StringUtils.isNotBlank(filterSessionSourceHeaderKey)) {
			this.sessionSourceHeaderKey = filterSessionSourceHeaderKey;
			this.hasSessionSourceHeaderKey = true;
		}
	}

	public static void removeSidInCookie(HttpServletResponse response){
		setSidInCookie(response,null);
	}
}
